home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1993 October: Windmill on DISC / ADC Developer CD (1993-10) (''Windmill On DISC'')_iso / Dev.CD Oct 93.iso / System Software / U.S. System Software / System 7 Pro™ Beta 11 / Development Tools / Sample Code / Standard Mail / MiniMailer / Src / MiniMailer.c next >
Encoding:
C/C++ Source or Header  |  1993-06-16  |  30.9 KB  |  1,235 lines  |  [TEXT/KAHL]

  1. /*                                MiniMailer.c                            */
  2. /*
  3.  * MiniMailer.c
  4.  * Copyright © 1992-93, Apple Computer Inc. All Rights Reserved.
  5.  *
  6.  * MiniMailer is an extremely simple mailer demo that allows you to
  7.  * send a TEXT file to a remote address. That's all it does. This
  8.  * demo is only intended to let you gain initial familiarity with
  9.  * the mailer; that's all it does. It is *not* a "real" application.
  10.  *
  11.  * The only, repeat, only intent of this demo is to show you the
  12.  * simplest-possible interaction with the standard mailer. See
  13.  * CollaboDraw for a more extensive real-world application.
  14.  *
  15.  * Oh, it has one other purpose: because it is so small, it can act as
  16.  * a test-bed for exploring the mailer's send functions.
  17.  *
  18.  * Author: Martin Minow, MACDTS.
  19.  * AppleLink:    DEVSUPPORT
  20.  *
  21.  */
  22. #define EXTERN    /* Nothing */
  23. #include "MiniMailer.h"
  24.  
  25. const SMPEditCommand    gMailerEditCommands[] = {
  26.     (SMPEditCommand) -1,    /* Edit menu title    */
  27.     kSMPUndoCommand,        /* kEditUndo        */
  28.     (SMPEditCommand) -1,    /* ---------        */
  29.     kSMPCutCommand,            /* kEditCut            */
  30.     kSMPCopyCommand,        /* kEditCopy        */
  31.     kSMPPasteCommand,        /* kEditPaste        */
  32.     kSMPClearCommand        /* kEditClear        */
  33. };
  34.  
  35. void
  36. main()
  37. {        
  38.         InitializeMacintosh();
  39.         SetCursor(*GetCursor(watchCursor));
  40.         gQuitNow = FALSE;
  41.         InitializeAppleEvents();
  42.         if (gQuitNow == FALSE)
  43.             InitializeMailer();
  44.         if (gQuitNow == FALSE)
  45.             BuildMailerWindow();
  46.         while (gQuitNow == FALSE) {
  47.             InitCursor();
  48.             SetPort(gMailerWindow);            /* Paranoia                    */
  49.             (void) WaitNextEvent(
  50.                     everyEvent,
  51.                     &gEventRecord,            /* -> Event Record            */
  52.                     6L,                        /* Sleep time                */
  53.                     NULL                    /* No mouse region            */
  54.                 );
  55.             /*
  56.              * First, pass all events to SMPMailerEvent. It will tell us
  57.              * whether we have to process the event or whether it has
  58.              * already been processed. Note that we even pass NULL events.
  59.              */
  60.             gStatus = SMPMailerEvent(
  61.                     &gEventRecord,            /* -> Event Record            */
  62.                     &gWhatHappened,            /* Result stored here        */
  63.                     NULL,                    /* No FrontWindow callback    */
  64.                     0                        /* No clientData            */
  65.                 );
  66.             /*
  67.              * If the user clicks on the same destination address twice,
  68.              * we'll get a kSMPAddressAlreadyInList status which we're
  69.              * happy to ignore.
  70.              */
  71.             if (gStatus == kSMPAddressAlreadyInList)
  72.                 gStatus = noErr;
  73.             /*
  74.              * If the user cancels an "Enclosure" or similar, SMPMailerEvent
  75.              * returns userCanceledErr. I'm not certain that this is correct.
  76.              */
  77.             if (1 && gStatus == userCanceledErr)
  78.                 gStatus = noErr;
  79.             if (gStatus != noErr)
  80.                 ErrorAlert(gStatus, "\pSMPMailerEvent error status");
  81.             else {
  82.                 if (gMailerWindow != NULL) {
  83.                     gStatus = SMPGetMailerState(gMailerWindow, &gMailerState);
  84.                     if (gStatus != noErr)
  85.                         ErrorAlert(gStatus, "\pSMPGetMailerState error");
  86.                     else if (gMailerChangeCount != gMailerState.changeCount) {
  87.                         Str255                work;
  88.                         
  89.                         gMailerChangeCount = gMailerState.changeCount;
  90.                         NumToString(gMailerChangeCount, work);
  91.                         SetWTitle(gMailerWindow, work);
  92.                     }
  93.                 }
  94.                 /*
  95.                  * If the mailer says that we should handle the event,
  96.                  * well, who are we to argue?
  97.                  */
  98.                 if ((gWhatHappened & kSMPAppMustHandleEventMask) != 0)
  99.                     DoApplicationEvent();
  100.                 if ((gWhatHappened & kSMPCreateCopyWindowMask) != 0) {
  101.                     gCopyInProgress = TRUE;
  102.                 }
  103.                 else if ((gWhatHappened & kSMPDisposeCopyWindowMask) != 0) {
  104.                     gCopyInProgress = FALSE;
  105.                 }
  106.                 CheckForDestination();
  107.                 AdjustMenus(FALSE);
  108.             }
  109.         }
  110.         /*
  111.          * Quitting.
  112.          */
  113.         if (gMailerWindow != NULL) {
  114.             /*
  115.              * This will fail if the user canceled the initial
  116.              * "password" dialog or the mailer couldn't be built.
  117.              */
  118.             gStatus = SMPDisposeMailer(gMailerWindow, FALSE);
  119.             if (gStatus != noErr
  120.              && gStatus != kSMPNoMailerInWindow)
  121.                 ErrorAlert(gStatus, "\pSMPDisposeMailer, unexpected error");
  122.             DisposeWindow(gMailerWindow);
  123.             gMailerWindow = NULL;
  124.         }
  125.         ExitToShell();
  126. }
  127.  
  128. /*
  129.  * DoApplicationEvent
  130.  * Process events that must be handled by the "ordinary" application.
  131.  * This function dispatches to some event-specific handlers, 
  132.  */
  133. void
  134. DoApplicationEvent(void)
  135. {
  136.         WindowPtr                theWindow;
  137.         Boolean                    isActivate;
  138.         
  139.         switch (gEventRecord.what) {
  140.         case kHighLevelEvent:
  141.             DoHighLevelEvent();
  142.             break;
  143.         case mouseDown:
  144.             DoApplicationMouseEvent();
  145.             break;
  146.         case activateEvt:
  147.             isActivate = (gEventRecord.modifiers & activeFlag);
  148.             theWindow = (WindowPtr) gEventRecord.message;
  149.             DoActivateEvent(theWindow, isActivate);
  150.             break;
  151.         case updateEvt:
  152.             DoUpdateEvent();
  153.             break;
  154.         case keyDown:
  155.         case autoKey:
  156.             DoKeyDownEvent();
  157.             break;
  158.         case osEvt:
  159.             /*
  160.              * Multifinder events.
  161.              */
  162.             switch ((unsigned long) gEventRecord.message >> 24) {
  163.             case mouseMovedMessage:
  164.                 break;                    /* Ignore: just do idle        */
  165.             case suspendResumeMessage:    /* Context switch            */
  166.                 isActivate = ((gEventRecord.message & resumeFlag) != 0);
  167.                 theWindow = FrontWindow();
  168.                 DoActivateEvent(theWindow, isActivate);
  169.                 break;
  170.             }
  171.             break;
  172.         default:                        /* Ignore other events        */
  173.             break;
  174.         }
  175. }
  176.  
  177. /*
  178.  * DoUpdateEvent
  179.  * Handle an update event in our window.
  180.  */
  181. void
  182. DoUpdateEvent(void)
  183. {
  184.         WindowPtr                theWindow;
  185.         GrafPtr                    savePort;
  186.  
  187.         theWindow = (WindowPtr) gEventRecord.message;
  188.         GetPort(&savePort);
  189.         SetPort(theWindow);
  190.         BeginUpdate(theWindow);
  191.         if (theWindow == gMailerWindow
  192.          && EmptyRgn((*theWindow).visRgn) == FALSE) {
  193.             EraseRect(&theWindow->portRect);
  194.             gStatus = SMPDrawMailer(theWindow);
  195.             if (gStatus != noErr)
  196.                 ErrorAlert(gStatus, "\pSMPDrawMailer, unexpected error");
  197.             DrawControls(theWindow);
  198.             DrawGrowIcon(theWindow);
  199.         }
  200.         EndUpdate(theWindow);
  201.         SetPort(savePort);
  202. }
  203.  
  204. /*
  205.  * DoKeyDown
  206.  * The only interesting keystrokes are for menu shortcuts.
  207.  * Everything else should have been handled by the mailer dialog.
  208.  */
  209. void
  210. DoKeyDownEvent(void)
  211. {
  212.         long                    menuChoice;
  213.  
  214.         /*
  215.          * Check for <CMD><PERIOD> and quit if so. This is
  216.          * not the right way to do this (it fails for non-US
  217.          * keyboards).
  218.          */
  219.         if ((gEventRecord.message & charCodeMask) == '.'
  220.          && (gEventRecord.modifiers & cmdKey) != 0) {
  221.              FlushEvents(keyDown | autoKey, 0);
  222.              gQuitNow = TRUE;
  223.         }
  224.         else if ((gEventRecord.modifiers & cmdKey) != 0) {
  225.             if (gEventRecord.what == keyDown) {
  226.                 /*
  227.                  * <CMD><Something>.
  228.                  */
  229.                 menuChoice = MenuKey(gEventRecord.message & charCodeMask);
  230.                 if (HiWord(menuChoice) != 0)
  231.                     DoMenuCommand(menuChoice);
  232.                 else {
  233.                     SysBeep(10);        /* Bogus <cmd>                    */
  234.                 }
  235.             }
  236.         }
  237.         else {
  238.             SysBeep(10);                /* Bogus keystroke                */
  239.         }
  240. }
  241.  
  242. /*
  243.  * DoActivateEvent
  244.  * Activate or deactivate the window. The mailer will handle this
  245.  * itself.
  246.  */
  247. void
  248. DoActivateEvent(
  249.         WindowPtr        theWindow,
  250.         Boolean            isActivate
  251.     )
  252. {
  253.         if (theWindow == gMailerWindow && isActivate) {
  254.             ShowWindow(theWindow);
  255.             SelectWindow(theWindow);
  256.         }
  257. }
  258.  
  259. /*
  260.  * DoApplicationMouseEvent
  261.  * The user clicked on something. This is either a menu event or
  262.  * a window drag/grow. Note that our windows don't have content
  263.  * other than the mailer.
  264.  */
  265. void
  266. DoApplicationMouseEvent(void)
  267. {
  268.         WindowPtr            theWindow;
  269.         register int        whichPart;
  270.         Rect                tempRect;
  271.         short                mailerWidth;
  272.         short                mailerContractedHeight;
  273.         short                mailerExpandedHeight;
  274.         long                newWindowSize;
  275.         
  276.         whichPart = FindWindow(gEventRecord.where, &theWindow);
  277.         if (whichPart == inMenuBar && theWindow != gMailerWindow)
  278.             theWindow = FrontWindow();
  279.         switch (whichPart) {
  280.         case inDesk:                    /* Ignore random clicks            */
  281.             break;
  282.         case inMenuBar:
  283.             DoMenuCommand(MenuSelect(gEventRecord.where));
  284.             break;
  285.         case inDrag:
  286.             tempRect = qd.screenBits.bounds;
  287.             tempRect.top += ((GetMBarHeight() + 1) * 2);
  288.             InsetRect(&tempRect, 4, 4);
  289.             DragWindow(theWindow, gEventRecord.where, &tempRect);
  290.             break;
  291.         case inGrow:
  292.             /*
  293.              * To grow the window, make sure that there is always
  294.              * space for the mailer. This isn't quite correct: if
  295.              * the mailer is contracted, we should allow the window
  296.              * to be set smaller (and grow it automatically if the
  297.              * user clicks on the expansion button).
  298.              */
  299.             gStatus = SMPGetDimensions(
  300.                     &mailerWidth,
  301.                     &mailerContractedHeight,
  302.                     &mailerExpandedHeight
  303.                 );
  304.             if (gStatus != noErr)
  305.                 ErrorAlert(gStatus, "\pSMPGetDimensions");
  306.             else {
  307.                 tempRect = qd.screenBits.bounds;
  308.                 tempRect.left = mailerWidth + kScrollBarSize;
  309.                 tempRect.top = mailerExpandedHeight + kScrollBarSize;
  310.                 newWindowSize = GrowWindow(
  311.                         gMailerWindow,
  312.                         gEventRecord.where,
  313.                         &tempRect
  314.                     );
  315.                 if (newWindowSize != 0) {
  316.                     SizeWindow(
  317.                         gMailerWindow,
  318.                         LoWord(newWindowSize),
  319.                         HiWord(newWindowSize),
  320.                         TRUE
  321.                     );
  322.                 }
  323.             }
  324.             break;
  325.         case inContent:
  326.             if (FrontWindow() != theWindow)
  327.                 SelectWindow(theWindow);
  328.             else {
  329.                 /*
  330.                  * Since we don't have any content, there isn't
  331.                  * anything interesting we can do here.
  332.                  */
  333.                 SetPort(theWindow);
  334.             }
  335.             break;
  336.         case inGoAway:
  337.             if (theWindow == gMailerWindow
  338.              && TrackGoAway(theWindow, gEventRecord.where)) {
  339.                  /*
  340.                   * Quitting: this should check for a pending
  341.                   * message to send and give the user a chance
  342.                   * to send it before quitting.
  343.                   */
  344.                  gQuitNow = TRUE;
  345.             }
  346.             break;
  347.         }
  348. }
  349.  
  350. /*
  351.  * DoMenuCommand
  352.  * Handle menu choices.
  353.  */
  354. void
  355. DoMenuCommand(
  356.         long            menuChoice
  357.     )
  358. {
  359.         WindowPtr            theWindow;
  360.         int                    menuItem;
  361.         GrafPtr                savePort;
  362.         Str255                name;
  363.  
  364.         theWindow = FrontWindow();
  365.         menuItem = LoWord(menuChoice);
  366.         switch (HiWord(menuChoice)) {
  367.         case MENU_Apple:
  368.             GetItem(gAppleMenu, menuItem, name);
  369.             if (menuItem == kAppleAbout)
  370.                 ; /* No about box    */
  371.             else {
  372.                 AdjustMenus(TRUE);
  373.                 GetPort(&savePort);
  374.                 OpenDeskAcc(name);
  375.                 SetPort(savePort);
  376.                 AdjustMenus(FALSE);
  377.             }
  378.             break;
  379.         case MENU_File:
  380.             switch (menuItem) {
  381.             case kFileSend:
  382.                 if (GetFileToSend())
  383.                     SendMailMessage();
  384.                 break;
  385.             case kFileTestGetListItemInfo:
  386.                 TestGetListItemInfo();
  387.                 break;
  388.             case kFileTestAddAttachment:
  389.                 TestAddAttachment();
  390.                 break;
  391.             case kFileTestNoTarget:
  392.                 gStatus = SMPGetMailerState(
  393.                         gMailerWindow,
  394.                         &gMailerState
  395.                     );
  396.                 if (gStatus != noErr)
  397.                     ErrorAlert(gStatus, "\pSMPGetMailerState, huh?");
  398.                 gLastTargetComponent = gMailerState.targetComponent;
  399.                 gStatus = SMPBecomeTarget(
  400.                         gMailerWindow,
  401.                         FALSE,
  402.                         0
  403.                     );
  404.                 if (gStatus != noErr)
  405.                     ErrorAlert(gStatus, "\pSMPBecomeTarget(FALSE), huh?");
  406.                 break;
  407.             case kFileTestBecomeTarget:
  408.                 gStatus = SMPBecomeTarget(
  409.                         gMailerWindow,
  410.                         TRUE,
  411.                         gLastTargetComponent
  412.                     );
  413.                 if (gStatus != noErr)
  414.                     ErrorAlert(gStatus, "\pSMPBecomeTarget(TRUE), huh?");
  415.                 break;
  416.             case kFileQuit:
  417.                 gQuitNow = TRUE;
  418.             case kFileForceQuit:
  419.                 /*
  420.                  * This tests what happens if you quit with an open
  421.                  * mailer window.
  422.                  */
  423.                 ExitToShell();
  424.             break;
  425.             }
  426.             break;
  427.         case MENU_Edit:
  428.             /*
  429.              * Since we don't have any data content, the user's
  430.              * menu choice must be for the mailer or a desk accessory.
  431.              */
  432.             gStatus = SMPMailerEditCommand(
  433.                     gMailerWindow,
  434.                     gMailerEditCommands[menuItem],
  435.                     &gWhatHappened
  436.                 );
  437.             if (gStatus != noErr)
  438.                 ErrorAlert(gStatus, "\pSMPMailerEditCommand, huh?");
  439.             else if ((gWhatHappened & kSMPAppMustHandleEventMask)) {
  440.                 /*
  441.                  * It's for the application. Check for a desk-accessory.
  442.                  */
  443.                 if (SystemEdit(menuItem - 1) == FALSE) {
  444.                     ErrorAlert(gStatus, "\pMiniMailer has no edit commands");
  445.                 }
  446.             }
  447.             break;
  448.         default:
  449.             break;
  450.         }
  451.         HiliteMenu(0);
  452.         AdjustMenus(FALSE);
  453.         InitCursor();
  454. }
  455.  
  456. /*
  457.  * AdjustMenus
  458.  * Fiddle with the menu options This doesn't quite work correctly.
  459.  * Perhaps we're not recognizing selections in the mailer area.
  460.  */
  461. void
  462. AdjustMenus(
  463.         Boolean            isDeskAccessory
  464.     )
  465. {
  466.         Boolean            enableUndo;
  467.         Boolean            enableCut;
  468.         Boolean            enableCopy;
  469.         Boolean            enablePaste;
  470.         Boolean            enableClear;
  471.         
  472.         EnableItem(gFileMenu, kFileQuit);
  473.         EnableItem(gFileMenu, kFileForceQuit);
  474.         if (isDeskAccessory) {
  475.             EnableItem(gEditMenu, kEditUndo);
  476.             EnableItem(gEditMenu, kEditCut);
  477.             EnableItem(gEditMenu, kEditCopy);
  478.             EnableItem(gEditMenu, kEditPaste);
  479.             EnableItem(gEditMenu, kEditClear);
  480.         }
  481.         else {
  482.             EnableItem(gFileMenu, kFileTestGetListItemInfo);
  483.             EnableItem(gFileMenu, kFileTestAddAttachment);
  484.             if (gMessageHasDestination && gCopyInProgress == FALSE)
  485.                 EnableItem(gFileMenu, kFileSend);
  486.             else {
  487.                 DisableItem(gFileMenu, kFileSend);
  488.             }
  489.             enableUndo    = FALSE;
  490.             enableCut    = FALSE;
  491.             enableCopy    = FALSE;
  492.             enablePaste    = FALSE;
  493.             enableClear    = FALSE;
  494.             gStatus = SMPGetMailerState(
  495.                     gMailerWindow,
  496.                     &gMailerState
  497.                 );
  498.             if (gStatus != noErr) {
  499.                 ErrorAlert(gStatus, "\pSMPGetMailerStatus, unexpected error");
  500.                 gMailerState.canCut = FALSE;
  501.                 gMailerState.canCopy = FALSE;
  502.                 gMailerState.canPaste = FALSE;
  503.                 gMailerState.canClear = FALSE;
  504.                 gMailerState.canSelectAll = FALSE;
  505.             }
  506.             if (gMailerState.isTarget) {
  507.                 enableUndo    = gMailerState.undoState == kSMPMailerUndo;
  508.                 enableCut    = gMailerState.canCut;
  509.                 enableCopy    = gMailerState.canCopy;
  510.                 enablePaste    = gMailerState.canPaste;
  511.                 enableClear    = gMailerState.canClear;
  512.                 if (enableUndo)
  513.                     SetItem(gEditMenu, kEditUndo, gMailerState.undoWhat);
  514.                 CheckItem(gFileMenu, kFileTestNoTarget, FALSE);
  515.                 CheckItem(gFileMenu, kFileTestBecomeTarget, TRUE);
  516.                 EnableItem(gFileMenu, kFileTestNoTarget);
  517.                 DisableItem(gFileMenu, kFileTestBecomeTarget);
  518.             }
  519.             else {
  520.                 CheckItem(gFileMenu, kFileTestNoTarget, TRUE);
  521.                 CheckItem(gFileMenu, kFileTestBecomeTarget, FALSE);
  522.                 DisableItem(gFileMenu, kFileTestNoTarget);
  523.                 EnableItem(gFileMenu, kFileTestBecomeTarget);
  524.             }
  525.             FixEditMenu(kEditUndo,    enableUndo);
  526.             FixEditMenu(kEditCut,    enableCut);
  527.             FixEditMenu(kEditCopy,    enableCopy);
  528.             FixEditMenu(kEditPaste,    enablePaste);
  529.             FixEditMenu(kEditClear,    enableClear);
  530.         }
  531. }
  532.  
  533. /*
  534.  * FixEditMenu
  535.  * Fiddle the edit menu.
  536.  */
  537. void
  538. FixEditMenu(
  539.         short                menuItem,
  540.         Boolean                enableIt
  541.     )
  542. {
  543.         if (enableIt)
  544.             EnableItem(gEditMenu, menuItem);
  545.         else {
  546.             DisableItem(gEditMenu, menuItem);
  547.         }
  548. }
  549.  
  550. /*
  551.  *** AppleEvent processing.
  552.  */
  553. typedef struct AEHandlerInstalls {
  554.     AEEventClass            eventClass;
  555.     AEEventID                eventID;
  556.     EventHandlerProcPtr        handlerProc;
  557. } AEHandlerInstalls;
  558. #define AEHandler(proc) \
  559.     pascal OSErr proc(AppleEvent *, AppleEvent *, long)
  560. AEHandler(AEOpenAppHandler);
  561. AEHandler(AEOpenDocHandler);
  562. AEHandler(AEPrintHandler);
  563. AEHandler(AEQuitHandler);
  564.  
  565. static AEHandlerInstalls    gAEHandlers[] = {
  566.     { kCoreEventClass,    kAEOpenApplication,    AEOpenAppHandler    },
  567.     { kCoreEventClass,    kAEOpenDocuments,    AEOpenAppHandler    },
  568.     { kCoreEventClass,    kAEPrintDocuments,    AEOpenAppHandler    },
  569.     { kCoreEventClass,    kAEQuitApplication,    AEOpenAppHandler    },
  570.     { 0,                0,                    NULL                }
  571. };
  572.  
  573. /*
  574.  * Install Apple Event handlers. Set gQuitNow on failure.
  575.  */
  576. void
  577. InitializeAppleEvents(void)
  578. {
  579.         long                    result;
  580.         AEHandlerInstalls        *hp;
  581.         
  582.         gStatus = Gestalt(gestaltAppleEventsAttr, &result);
  583.         if (gStatus != noErr)
  584.             ErrorAlert(gStatus, "\pWhat, No AppleEvents?");
  585.         else {
  586.             for (hp = gAEHandlers; hp->handlerProc != NULL; hp++) {
  587.                 gStatus = AEInstallEventHandler(
  588.                             hp->eventClass,
  589.                             hp->eventID,
  590.                             hp->handlerProc,
  591.                             0,
  592.                             FALSE
  593.                         );
  594.                 if (gStatus != noErr) {
  595.                     ErrorAlert(gStatus, "\pCan't install AppleEvent");
  596.                     break;
  597.                 }
  598.             }
  599.         }
  600.         if (gStatus != noErr)
  601.             gQuitNow = TRUE;
  602. }
  603.  
  604. void
  605. DoHighLevelEvent(void)
  606. {
  607.         (void) AEProcessAppleEvent(&gEventRecord);
  608. }
  609.  
  610. /*
  611.  * Apple event handlers. Mostly return errors.
  612.  */
  613. pascal OSErr
  614. AEOpenAppHandler(
  615.         AppleEvent            *eventMessage,
  616.         AppleEvent            *eventReply,
  617.         long                refCon
  618.     )
  619. {
  620. #pragma unused (eventMessage, eventReply, refCon)
  621.         return (noErr);
  622. }
  623.  
  624. pascal OSErr
  625. AEOpenDocHandler(
  626.         AppleEvent            *eventMessage,
  627.         AppleEvent            *eventReply,
  628.         long                refCon
  629.     )
  630. {
  631. #pragma unused (eventMessage, eventReply, refCon)
  632.         return (errAEEventNotHandled);
  633. }
  634.  
  635. pascal OSErr
  636. AEPrintHandler(
  637.         AppleEvent            *eventMessage,
  638.         AppleEvent            *eventReply,
  639.         long                refCon
  640.     )
  641. {
  642. #pragma unused (eventMessage, eventReply, refCon)
  643.         return (errAEEventNotHandled);
  644. }
  645.  
  646. pascal OSErr
  647. AEQuitHandler(
  648.         AppleEvent            *eventMessage,
  649.         AppleEvent            *eventReply,
  650.         long                refCon
  651.     )
  652. {
  653. #pragma unused (eventMessage, eventReply, refCon)
  654.         gQuitNow = TRUE;
  655.         return (noErr);
  656. }
  657.  
  658.  
  659. /*
  660.  * InitializeMacintosh
  661.  * Standard application initialization
  662.  */
  663. void
  664. InitializeMacintosh()
  665. {
  666.         short                    i;
  667.         EventRecord                thisEvent;
  668.         long                    response;
  669.         
  670.         MaxApplZone();
  671.         InitGraf(&qd.thePort);
  672.         InitFonts();
  673.         InitWindows();
  674.         InitMenus();
  675.         TEInit();
  676.         InitDialogs(NULL);
  677.         HNoPurge((Handle) GetCursor(watchCursor));
  678.         SetCursor(*GetCursor(watchCursor));
  679.         for (i = 0; i < 3; i++)
  680.             EventAvail(everyEvent, &thisEvent);
  681.         SetMenuBar(GetNewMBar(MBAR_MenuBar));
  682.         gAppleMenu = GetMHandle(MENU_Apple);
  683.         AddResMenu(gAppleMenu, 'DRVR');
  684.         gFileMenu = GetMHandle(MENU_File);
  685.         gEditMenu = GetMHandle(MENU_Edit);
  686.         DrawMenuBar();
  687.         if (Gestalt(gestaltQuickdrawVersion, &response) == noErr
  688.          && response >= gestalt8BitQD)
  689.              gHasColorQuickDraw = TRUE;
  690.         InitCursor();
  691. }
  692.  
  693. /*
  694.  * The following functions handle AOCE Standard Mail stuff.
  695.  */
  696.  
  697.  
  698. /*
  699.  * One-time initialization for the mailer. gQuitNow is set TRUE
  700.  * on failure.
  701.  */
  702. void
  703. InitializeMailer(void)
  704. {
  705.         /*
  706.          * Initialize the mailer and create the mailer window.
  707.          */
  708.         gStatus = SystemSupportsAOCE();
  709.         switch (gStatus) {
  710.         case kOCEToolboxNotOpen:
  711.             ErrorAlert(gStatus, "\pAOCE Mailer is not available");
  712.             break;
  713.         case gestaltUndefSelectorErr:
  714.             ErrorAlert(gStatus, "\pAOCE is not installed on this machine");
  715.             break;
  716.         case noErr:
  717.             break;
  718.         default:
  719.             ErrorAlert(gStatus, "\pGestalt: unexpected error");
  720.         }
  721.         if (gStatus != noErr)
  722.             gQuitNow = TRUE;
  723.         else {
  724.             gStatus = GetUserIdentity(&gUserIdentity);
  725.             if (gStatus != noErr) {
  726.                 if (gStatus != userCanceledErr)
  727.                     ErrorAlert(gStatus, "\pGetUserIdentity");
  728.                 gQuitNow = TRUE;
  729.             }
  730.         }
  731.         if (gQuitNow == FALSE) {
  732. #if OCE_Beta2
  733.             gStatus = SMPInitMailer(0);
  734. #else
  735.             gStatus = SMPInitMailer();
  736. #endif
  737.             if (gStatus != noErr) {
  738.                 InitCursor();
  739.                 ErrorAlert(gStatus, "\pSMPInitMailer: no mail support");
  740.                 gQuitNow = TRUE;            /* No point going further    */
  741.             }
  742.         }
  743. }
  744.  
  745. /*
  746.  * BuildMailerWindow
  747.  * Create a window containing a standard mailer.
  748.  */
  749. void
  750. BuildMailerWindow(void)
  751. {
  752.         Rect                boundsRect;
  753.         Point                mailerCorner;
  754.         short                mailerWidth;
  755.         short                mailerContractedHeight;
  756.         short                mailerExpandedHeight;
  757.  
  758.         boundsRect = qd.screenBits.bounds;
  759.         boundsRect.top += ((GetMBarHeight() + 1) * 2);
  760.         InsetRect(&boundsRect, 4, 4);
  761.         gStatus = SMPGetDimensions(
  762.                 &mailerWidth,
  763.                 &mailerContractedHeight,
  764.                 &mailerExpandedHeight
  765.             );
  766.         if (gStatus != noErr)
  767.             ErrorAlert(gStatus, "\pSMPGetDimensions");
  768.         else {
  769.             boundsRect.right = boundsRect.left + mailerWidth;
  770.             boundsRect.bottom = boundsRect.top + mailerExpandedHeight;
  771.         }
  772.         /*
  773.          * Create the mailer window. Change "1" to "0" to force
  774.          * a black/white window on a color system for testing.
  775.          */
  776.         if (1 && gHasColorQuickDraw) {
  777.             gMailerWindow = NewCWindow(
  778.                     NULL,                /* No window storage        */
  779.                     &boundsRect,        /* Window shape                */
  780.                     "\pMiniMailer",        /* Window title                */
  781.                     TRUE,                /* Visible                    */
  782.                     documentProc,        /* Document, no zoom button    */
  783.                     (WindowPtr) -1L,    /* In front                    */
  784.                     TRUE,                /* Has goAway                */
  785.                     0                    /* refCon (ignored)            */
  786.                 );
  787.         }
  788.         else {
  789.             gMailerWindow = NewWindow(
  790.                     NULL,                /* No window storage        */
  791.                     &boundsRect,        /* Window shape                */
  792.                     "\pMiniMailer",        /* Window title                */
  793.                     TRUE,                /* Visible                    */
  794.                     documentProc,        /* Document, no zoom button    */
  795.                     (WindowPtr) -1L,    /* In front                    */
  796.                     TRUE,                /* Has goAway                */
  797.                     0                    /* refCon (ignored)            */
  798.                 );
  799.         }
  800.         if (gMailerWindow == NULL) {
  801.             ErrorAlert(MemError(), "\pNewWindow (fatal)");
  802.             ExitToShell();
  803.         }
  804.         SetPort(gMailerWindow);            /* Paranoia                    */
  805.         SetPt(&mailerCorner, 0, 0);        /* Location within window    */
  806.         gStatus = SMPNewMailer(            /* Create Standard Mailer    */
  807.                 gMailerWindow,            /* In this window            */
  808.                 mailerCorner,            /* Mailer top-left            */
  809.                 FALSE,                    /* Cannot contract            */
  810.                 TRUE,                    /* Initially expanded        */
  811.                 gUserIdentity,            /* Default identity            */
  812.                 NULL,                    /* No prepare callback        */
  813.                 0                        /* No client data            */
  814.             );
  815.         if (gStatus != noErr) {
  816.             ErrorAlert(gStatus, "\pSMPNewMailer (fatal)");
  817.             ExitToShell();
  818.         gLastTargetComponent = kSMPOther;
  819.         gStatus = SMPBecomeTarget(
  820.                 gMailerWindow,
  821.                 TRUE,
  822.                 gLastTargetComponent
  823.             );
  824.         if (gStatus != noErr)
  825.             ErrorAlert(gStatus, "\pSMPBecomeTarget(TRUE) at create, huh?");
  826.         }
  827. }
  828.  
  829. /*
  830.  * CheckForDestination
  831.  * This is called whenever the mailer window content changes. It
  832.  * checks to see whether there is a destination, setting the global
  833.  * flag as needed. The "File/Send" menu is enabled if (and only if)
  834.  * at least one destination has been specified.
  835.  */
  836. void
  837. CheckForDestination(void)
  838. {
  839.         unsigned short            fieldSize;
  840.         
  841.         gStatus = SMPGetComponentSize(
  842.                 gMailerWindow,                /* In this window            */
  843.                 1,                            /* First (and only) mailer    */
  844.                 kSMPTo,                        /* The destination field    */
  845.                 &fieldSize                    /* Result                    */
  846.             );
  847.         if (gStatus != noErr) {
  848.             ErrorAlert(gStatus, "\pSMPGetComponentSize");
  849.             fieldSize = 0;
  850.         }
  851.         gMessageHasDestination = (fieldSize != 0);
  852. }            
  853.  
  854.  
  855. /*
  856.  * TestGetListItemInfo
  857.  * This is a temporary test function to see what happens if you call
  858.  * SMPGetListItemInfo when nothing is selected in the to field.
  859.  * This subroutine makes no sense unless you run it with debug
  860.  * traps set appropriately. This test determined that SMPGetListItemInfo
  861.  * returns a paramErr if the user hasn't set anything in the "to" field.
  862.  * The documentation states that SMPGetComponentSize should be called
  863.  * first. If it returns "zero" size, nothing is in the "to" field.
  864.  */
  865.  unsigned char        gScratchBuffer[4096];
  866.  
  867.  void
  868.  TestGetListItemInfo(void)
  869.  {
  870.          unsigned short            startItem;
  871.          unsigned short            itemCount;
  872.          unsigned short            nextItem;
  873.          Boolean                    moreFlag;
  874.          
  875.          for (startItem = 0, moreFlag = TRUE;
  876.                  moreFlag;
  877.                  startItem = nextItem) {
  878.             gStatus = SMPGetListItemInfo(
  879.                     gMailerWindow,            /* In this window            */
  880.                     1,                        /* First (and only) mailer    */
  881.                     kSMPTo,                    /* The destination field    */
  882.                     gScratchBuffer,            /* Temp buffer                */
  883.                     sizeof gScratchBuffer,    /* Temp buffer size            */
  884.                     0,                        /* Start item                */
  885.                     &itemCount,                /* Item counter                */
  886.                     &nextItem,                /* Next item to be returned    */
  887.                     &moreFlag                /* Is there more to do?        */
  888.                 );
  889.             if (gStatus != noErr) {
  890.                 ErrorAlert(gStatus, "\pSMPGetListItemInfo");
  891.                 break;
  892.             }
  893.         }
  894. }
  895.  
  896. /*
  897.  * Test the AddAttachment function.
  898.  */
  899. void
  900. TestAddAttachment(void)
  901. {
  902.         StandardFileReply        reply;
  903.         
  904.         StandardGetFile(NULL, -1, NULL, &reply);
  905.         if (reply.sfGood) {
  906.             gStatus = SMPAddAttachment(
  907.                         gMailerWindow,
  908.                         &reply.sfFile
  909.                     );
  910.             if (gStatus != noErr)
  911.                 ErrorAlert(gStatus, "\pSMPAddAttachment");
  912.         }
  913. }
  914.  
  915. /*
  916.  * SendMailMessage
  917.  * This is a modal procedure that sends the current message.
  918.  */
  919. void
  920. SendMailMessage(void)
  921. {
  922.         Boolean            sendWasSuccessful;
  923.         StringPtr        nativeFormatNames[1];
  924.         SMPSendFormat    currentFormat;
  925.         
  926.         nativeFormatNames[0] = "\pMiniMailer";
  927.         currentFormat.whichFormats = kSMPStandardInterchangeMask;
  928.         currentFormat.whichNativeFormat = 0;
  929.         sendWasSuccessful = FALSE;
  930.         gStatus = SMPSendOptionsDialog(
  931.                 gMailerWindow,            /* The window                */
  932.                 gMailDocumentTitle,        /* Dialog title                */
  933.                 nativeFormatNames,        /* Default native fmt names    */
  934.                 1,                        /* Native format name count    */
  935.                 kSMPStandardInterchangeMask, /* Just text, please    */
  936.                 ¤tFormat,            /* Current format            */
  937.                 NULL,                    /* No filter porc            */
  938.                 0,                        /* No Client data            */
  939.                 &gSendFormat,            /* Should send (result)        */
  940.                 &gSendOptions            /* (result)                    */
  941.             );
  942.         if (gStatus != noErr) {
  943.             ErrorAlert(gStatus, "\pSMPSendOptionsDialog");
  944.             goto exit;
  945.         }
  946.         if (gSendFormat.whichFormats == 0) /* Cancel?                */
  947.             goto exit;
  948.         SetCursor(*GetCursor(watchCursor));
  949.         gStatus = SMPBeginSend(
  950.                 gMailerWindow,
  951.                 kMailAppleMailCreator,    /* Creator (Letter app)        */
  952.                 kMailLtrMsgType,        /* File type                */
  953. #if OCE_Beta2
  954.                 &gSendOptions,            /* Send Options Record        */
  955. #else
  956.                 gSendOptions,            /* Send Options Handle        */
  957. #endif
  958.                 &gMustAddContent        /* Set by SMPBeginSend        */
  959.             );
  960.         if (gStatus != noErr) {
  961.             ErrorAlert(gStatus, "\pSMPBeginSend");
  962.             goto endSend;
  963.         }
  964.         if (gMustAddContent)            /* Anything to add?            */
  965.             sendWasSuccessful = SendThisFile();    /* Send the file    */
  966. endSend:
  967.         gStatus = SMPEndSend(gMailerWindow, sendWasSuccessful);
  968.         if (gStatus != noErr) {
  969.             ErrorAlert(gStatus, "\pSMPEndSend");
  970.             goto exit;
  971.         }
  972. exit:    CloseDataFile();
  973. }
  974.  
  975. /*
  976.  * SendThisFile
  977.  * Read a file and stuff it into the mail message. Return TRUE
  978.  * if the process completed correctly. This is needed by SMPEndSend
  979.  * which will delete everything if SendThisFile failed.
  980.  *
  981.  * A real application would display a progress thermometer.
  982.  */
  983. Boolean
  984. SendThisFile(void)
  985. {
  986.         Boolean                    sendWasSuccessful;
  987.         Boolean                    startNewScript;
  988.         Boolean                    appendFlag;
  989.         
  990.         /*
  991.          * This loop reads data from the file and adds it to
  992.          * the mail message.
  993.          *    startNewScript        TRUE for the first call, then FALSE for
  994.          *                        successive calls unless you need to
  995.          *                        change the script code.
  996.          *    appendFlag            FALSE for the first call, then TRUE
  997.          *                        for successive calls unless you need
  998.          *                        to start a new segment (i.e., change
  999.          *                        from sending text to sending pictures).
  1000.          * Since this demo has only one script and one segment type,
  1001.          * the flags are fixed in nature.
  1002.          */        
  1003.         sendWasSuccessful = FALSE;
  1004.         startNewScript = TRUE;
  1005.         appendFlag = FALSE;
  1006.         while (gFileAtEOF == FALSE) {
  1007.             ReadDataFromFile();
  1008.             gStatus = SMPAddContent(
  1009.                     gMailerWindow,            /* The mailer window    */
  1010.                     kMailTextSegmentType,    /* This is text            */
  1011.                     appendFlag,                /* Append to current    */
  1012.                     gFileDataBuffer,        /* The data itself        */
  1013.                     gFileByteCount,            /* Data segment size    */
  1014.                     NULL,                    /* Unstyled text        */
  1015.                     startNewScript,            /* New script            */
  1016.                     smRoman                    /* Script code            */
  1017. #if OCE_Beta2 == FALSE
  1018.                     , gFileAtEOF            /* Last block            */
  1019. #endif
  1020.                 );
  1021.             if (gStatus != noErr) {
  1022.                 ErrorAlert(gStatus, "\pSMPAddContent");
  1023.                 goto endSend;
  1024.             }
  1025.             startNewScript = FALSE;            /* Use same script        */
  1026.             appendFlag = TRUE;                /* Continue in segment    */
  1027.         }
  1028.         sendWasSuccessful = TRUE;
  1029. endSend:
  1030.         return (sendWasSuccessful);
  1031. }
  1032.  
  1033. /*
  1034.  * GetFileToSend
  1035.  * Use the standard "Open..." dialog to get a text file to send.
  1036.  * Return TRUE if there's a file, FALSE if the user Cancels.
  1037.  * If successful, gFileRefNum has the file reference number.
  1038.  */
  1039. Boolean
  1040. GetFileToSend(void)
  1041. {
  1042.         SFReply                reply;
  1043.         Point                where;
  1044.         SFTypeList            typeList;
  1045.         Boolean                result;
  1046.         
  1047.         typeList[0] = 'TEXT';
  1048.         SetPt(&where, 20, 80);
  1049.         SFGetFile(
  1050.             where,                        /* Window location        */
  1051.             NULL,                        /* No prompt            */
  1052.             NULL,                        /* No filter            */
  1053.             1,                            /* One file type        */
  1054.             typeList,                    /* Vector of file types    */
  1055.             NULL,                        /* No dialog hook        */
  1056.             &reply                        /* File stuff goes here    */
  1057.         );
  1058.         result = reply.good;            /* False if Cancel hit    */
  1059.         if (result) {
  1060.             pstrcpy(gMailDocumentTitle, reply.fName);
  1061.             gStatus = FSOpen(
  1062.                     reply.fName,        /* The specified file    */
  1063.                     reply.vRefNum,        /* On this volume        */
  1064.                     &gFileRefNum        /* Here's the file id    */
  1065.                 );
  1066.             if (gStatus != noErr) {
  1067.                 ErrorAlert(gStatus, "\pFSOpen, couldn't open");
  1068.                 gFileRefNum = (-1);
  1069.                 result = FALSE;
  1070.             }
  1071.         }
  1072.         if (result) {
  1073.             gFileDataBuffer = NewPtr(kFileReadSize);
  1074.             if (gFileDataBuffer != NULL)
  1075.                 gFileAtEOF = FALSE;
  1076.             else {
  1077.                 ErrorAlert(MemError(), "\pNewPtr(data buffer)");
  1078.                 CloseDataFile();
  1079.                 result = FALSE;
  1080.             }
  1081.         }
  1082.         return (result);
  1083. }
  1084.  
  1085. /*
  1086.  * ReadDataFromFile
  1087.  * Read a chunk of data from the file. set gFileAtEOF if we
  1088.  * read the end of file. Note that there may be data in the
  1089.  * last record read (i.e. even if gFileAtEOF is TRUE).
  1090.  *    gFileDataBuffer    contains the data.
  1091.  *    gFileByteCount    the number of bytes read
  1092.  *    gFileAtEOF        TRUE at end of file or error
  1093.  */ 
  1094. void
  1095. ReadDataFromFile(void)
  1096. {        
  1097.         gFileByteCount = kFileReadSize;
  1098.         gStatus = FSRead(
  1099.                 gFileRefNum,
  1100.                 &gFileByteCount,
  1101.                 gFileDataBuffer
  1102.             );
  1103.         if (gStatus != noErr) {
  1104.             gFileAtEOF = TRUE;
  1105.             if (gStatus != eofErr)            /* EOF is expected    */
  1106.                 ErrorAlert(gStatus, "\pFSRead");
  1107.         }
  1108. }
  1109.  
  1110. /*
  1111.  * CloseDataFile
  1112.  * Close the data file.
  1113.  */
  1114. void
  1115. CloseDataFile(void)
  1116. {
  1117.         gStatus = FSClose(gFileRefNum);
  1118.         if (gStatus != noErr)
  1119.             ErrorAlert(gStatus, "\pFSClose");
  1120.         gFileRefNum = (-1);
  1121.         if (gFileDataBuffer != NULL) {
  1122.             DisposePtr(gFileDataBuffer);
  1123.             gFileDataBuffer = NULL;
  1124.         }
  1125. }
  1126.  
  1127. /*
  1128.  * Get the user's local identity. On success, the identity is
  1129.  * stored in the specified location. The only expected error
  1130.  * is userCanceledErr, which probably means "just exit the
  1131.  * application." Other errors should be displayed.
  1132.  */
  1133. OSErr
  1134. GetUserIdentity(
  1135.         AuthIdentity            *userIdentity
  1136.     )
  1137. {
  1138.         long                    gestaltResponse;
  1139.         SDPIdentityKind            selectedKind;
  1140.         AuthParamBlock            authParamBlock;
  1141.         OSErr                    status;
  1142.         
  1143.         /*
  1144.          * Make sure this Macintosh supports AOCE.
  1145.          */
  1146.         status = Gestalt(gestaltOCEToolboxAttr, &gestaltResponse);
  1147.         if (status == noErr
  1148.          && (gestaltResponse & gestaltOCETBAvailable) == 0)
  1149.             status = kOCEToolboxNotOpen;
  1150.         if (status == noErr) {
  1151.             /*
  1152.              * OCE Setup: get the user's local authentication identity. If it's
  1153.              * already set, we can do this silently. If it's not set (the system
  1154.              * has just been started), we'll prompt for the user's identity
  1155.              * and password.
  1156.              *
  1157.              * The only expected error is "Cancel" from SDPPromptForIdentity.
  1158.              */
  1159.             status = AuthGetLocalIdentity(
  1160.                     &authParamBlock,            /* Parameter block                    */
  1161.                     FALSE                        /* Synchronous                        */
  1162.                 );
  1163.         }
  1164.         if (status == noErr) {
  1165.             /*
  1166.              * The user identity has already been specified.
  1167.              */
  1168.             *userIdentity = authParamBlock.getLocalIdentityPB.theLocalIdentity;
  1169.         }
  1170.         else if (status == kOCELocalAuthenticationFail) {
  1171.             /*
  1172.              * No user identity has been specified. Ask for the user to specify
  1173.              * the local identity.
  1174.              */
  1175.             status = SDPPromptForID(
  1176.                     userIdentity,            /* AuthIdentity        *id                */ 
  1177.                     NULL,                    /* Default guest prompt                */
  1178.                     NULL,                    /* Default specific prompt            */
  1179.                     NULL,                    /* Default local prompt                */
  1180.                     NULL,                    /* RString            *recordType        */
  1181.                     (                        /* SDPIdentityKind    permittedKinds    */
  1182.                         kSDPLocalIdentityMask        /* Local identity            */
  1183.                         | kSDPSpecificIdentityMask    /* or specific identity        */
  1184.                     ),
  1185.                     &selectedKind,            /* SDPIdentityKind    *selectedKind    */
  1186.                     NULL,                    /* RecordID            *loginFilter    */
  1187.                     0                        /* SDPLoginFilterKind filterKind    */                        
  1188.                 );
  1189.         }
  1190.         else {
  1191.             /* AuthGetLocalIdentity is very unhappy. */
  1192.         }
  1193.         return (status);
  1194. }
  1195.  
  1196. /*
  1197.  * Test for the presence of AOCE.
  1198.  * Return noErr                        Installed and available.
  1199.  * Return kOCEToolboxNotOpen         Installed, but not available,
  1200.  * Return gestaltUndefSelectorErr    Not present on this machine
  1201.  */
  1202. OSErr
  1203. SystemSupportsAOCE(void)
  1204. {
  1205.         long                    gestaltResponse;
  1206.         OSErr                    status;
  1207.         
  1208.         status = Gestalt(gestaltOCEToolboxAttr, &gestaltResponse);
  1209.         if (status == noErr
  1210.          && (gestaltResponse & gestaltOCETBAvailable) == 0)
  1211.             status = kOCEToolboxNotOpen;
  1212.         return (status);
  1213. }
  1214. /*
  1215.  * ErrorAlert
  1216.  * Display an error message.
  1217.  */
  1218. void
  1219. ErrorAlert(
  1220.         OSErr                    status,
  1221.         StringPtr                messageText
  1222.     )
  1223. {
  1224.         Str15                        errorValue;
  1225.         
  1226.         NumToString(status, errorValue);
  1227.         ParamText(
  1228.             errorValue,
  1229.             messageText,
  1230.             "\p",
  1231.             "\p"
  1232.         );
  1233.         InitCursor();
  1234.         StopAlert(ALRT_Error, NULL);
  1235. }